package xyz.weblade.beans.factory.support;

import xyz.weblade.beans.BeansException;
import xyz.weblade.beans.factory.DisposableBean;
import xyz.weblade.beans.factory.ObjectFactory;
import xyz.weblade.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 单例池默认具体实现(增加了缓存)
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    /**
     * null singleton对象的内部标记：用作并发映射（不支持null值）的标记值。
     */
    protected static final Object NULL_OBJECT = new Object();
    //（一级缓存）单例池 没有加final，是因为作者希望能够灵活的替换这个池子
    private Map<String,Object> singletonObjects = new HashMap<>();

    //（二级缓存）早期对象缓存 提前暴露对象 没有完全实例化的对象
    protected final Map<String,Object> earlySingletonObjects = new HashMap<>();
    //工厂缓存 提前暴露代理对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();


    //销毁方法的缓存  这里用了final，是因为spring不希望这个重新指向新的集合。这个一千个哈姆雷特解读
    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();


    @Override
    public Object getSingleton(String beanName) {
        Object singletonObject = singletonObjects.get(beanName);
        if (null == singletonObject) {
            singletonObject = earlySingletonObjects.get(beanName);
            // 判断早期缓存中是否有提前暴露的对象
            if (null == singletonObject) {
                //没有就去找工厂对象获取FactoryBean类型
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 把工厂对象中拿到的Bean先放到提前暴露的缓存
                    earlySingletonObjects.put(beanName, singletonObject);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName,singletonObject);
        earlySingletonObjects.remove(beanName,singletonObject);
        singletonFactories.remove(beanName);
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }

}
